package com.example.redissondemo.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.type.TypeFactory;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.IMarkerFactory;
import org.slf4j.Marker;
import org.slf4j.helpers.BasicMarkerFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Jackson的工具
 *
 * @author Liunh
 */
@Slf4j
public class JacksonUtil {

    /** 默认日期时间格式 */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /** 默认日期格式 */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /** 默认时间格式 */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
    /**
     * 页面传至后台时，json数据在request的参数名称
     */
    public final static String JSON_ATTRIBUTE = "json";
    public final static String JSON_ATTRIBUTE1 = "json1";
    public final static String JSON_ATTRIBUTE2 = "json2";
    public final static String JSON_ATTRIBUTE3 = "json3";
    public final static String JSON_ATTRIBUTE4 = "json4";

    public static ObjectMapper mapper;
    public static Marker jsonMarker;

    static {
        mapper = new ObjectMapper();
        //mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        //mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        //mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        //JavaTimeModule javaTimeModule = new JavaTimeModule();
        //javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        //javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        //javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        //javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        //javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        //javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        //mapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
        //mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.configure(SerializationFeature.INDENT_OUTPUT, false);
        mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        mapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
        mapper.configure(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN, true);
        mapper.configure(JsonGenerator.Feature.IGNORE_UNKNOWN, true);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    private JacksonUtil() {
        final IMarkerFactory markerFactory = new BasicMarkerFactory();
        jsonMarker = markerFactory.getMarker("jsonMarker");
    }

    /**
     * 将json string反序列化成非集合对象
     *
     * @param json
     * @param clazz
     * @return
     */
    public static <T> T jsonToBean(String json, Class<T> clazz) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readValue(json, clazz);
        } catch (IOException e) {
            log.error(jsonMarker, "将json字符串：" + json + "转换成" + String.valueOf(clazz) + "时发生异常。", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为list对象
     *
     * @param json
     * @param clazz 泛型类型
     * @return
     */
    public static <T> List<T> jsonToList(String json, Class<T> clazz) {
        final JavaType javaType = getCollectionType(ArrayList.class, clazz);
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readValue(json, javaType);
        } catch (IOException e) {
            log.error(">>>>>> 将json数组：" + json + "转换成" + String.valueOf(clazz) + "list对象时发生异常。", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为array对象
     *
     * @param json
     * @param clazz 数组类型
     * @return
     */
    public static <T> T[] jsonToArray(String json, Class<T[]> clazz) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readValue(json, clazz);
        } catch (IOException e) {
            log.error(">>>>>> 将json数组：" + json + "转换成" + String.valueOf(clazz) + "数组对象时发生异常。", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为map对象
     *
     * @param json
     * @param clazz value类型
     * @return
     */
    public static <T> Map<String, T> jsonToMap(String json, Class<T> clazz) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readValue(json, getCollectionType(Map.class, String.class, clazz));
        } catch (IOException e) {
            log.error(">>>>>> 将json字符串：" + json + "转换成Map<String," + String.valueOf(clazz) + ">时发生异常。", e);
        }
        return null;
    }

    /**
     * 将json array反序列化为map对象
     *
     * @param json
     * @param clazz value类型
     * @return
     */
    public static <T> Map<String, T> jsonToMap(String json, Class<T> clazz, JavaType javaType) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            if (javaType == null) {
                javaType = getCollectionType(Map.class, String.class, clazz);
            }
            return mapper.readValue(json, javaType);
        } catch (IOException e) {
            log.error(">>>>>> 将json字符串：" + json + "转换成Map<String," + String.valueOf(clazz) + ">时发生异常。", e);
        }
        return null;
    }

    /**
     * 将对象序列化为json字符串
     *
     * @param obj
     * @return
     */
    public static String beanToJson(Object obj) {
        try {
            if (obj == null) {
                return null;
            }
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error(">>>>>> 将对象：{} 转换成json时发生异常", obj, e);
        }
        return null;
    }

    /**
     * 将对象序列化为json字符串
     *
     * @param obj
     * @return
     */
    public static String beanToJson(Object obj, String pattern) {
        try {
            if (obj == null) {
                return null;
            }
            mapper.setDateFormat(new SimpleDateFormat(pattern));
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error(">>>>>> 将对象：{} 转换成json时发生异常", obj, e);
        }
        return null;
    }

    /**
     * 将json字符串转换为jsonNode对象
     *
     * @param json
     * @return
     */
    public static JsonNode jsonToNode(String json) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readTree(json);
        } catch (IOException e) {
            log.error(">>>>>> 将json字符串：{} 转换成jsonNode发生异常。", json, e);
        }
        return null;
    }

    /**
     * 将jsonNode字符串转换为JavaBean对象
     *
     * @param node
     * @return
     */
    public static <T> T nodeToBean(JsonNode node, Class<T> clazz) {
        try {
            if (node == null) {
                return null;
            }
            return mapper.treeToValue(node, clazz);
        } catch (IOException e) {
            log.error(">>>>>> JsonNode：" + node + "转换成" + String.valueOf(clazz) + "时发生异常。", e);
        }
        return null;
    }

    /**
     * 获取collection带泛型的对象
     *
     * @param collectionClass
     * @param elementClasses  不定参，按顺序输入泛型类型
     * @return
     */
    public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    /**
     * <p>转换方法，转换为字符串，并返回</p>
     */
    public static String convert(Object object) {
        if (object == null) {
            return null;
        }
        try {
            return mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error(">>>>>> 转换对象异常", e);
        }
        return null;
    }

    /**
     * <p>转换json为对象</p>
     */
    public static <T> T convert(String json, Class<T> classz) {
        if (json == null) {
            return null;
        }
        try {
            return mapper.readValue(json, classz);
        } catch (IOException e) {
            log.error(">>>>>> 转换对象异常", e);
        }
        return null;
    }

    /**
     * <p>转换json为复杂对象javaType类型</p>
     */
    public static <T> T convert(String json, JavaType javaType) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            return mapper.readValue(json, javaType);
        } catch (IOException e) {
            log.error(">>>>>> 转换对象异常", e);
        }
        return null;
    }

    /**
     * <p>转换json为复杂对象javaType类型</p>
     */
    public static <T> T convert(String json, Class<? extends Collection> collectionClass, Class<?> elementClas) {
        try {
            if (StringUtils.isBlank(json)) {
                return null;
            }
            final JavaType javaType = JacksonUtil.createJavaType(collectionClass, elementClas);
            return mapper.readValue(json, javaType);
        } catch (IOException e) {
            log.error(">>>>>> 转换对象异常", e);
        }
        return null;
    }

    /**
     * <p>构造javaType类型</p>
     */
    public static JavaType createJavaType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
        return mapper.getTypeFactory().constructCollectionType(collectionClass, elementClass);
    }

    /**
     * <p>构造javaType类型</p>
     */
    public static JavaType createJavaType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
        return mapper.getTypeFactory().constructMapType(mapClass, keyClass, valueClass);
    }

    /**
     * <p>类型构造器工厂</p>
     */
    public static TypeFactory getTypeFactory() {
        return mapper.getTypeFactory();
    }
}
